package com.lsec.springbootinit.filter;

import cn.hutool.core.convert.NumberWithFormat;
import cn.hutool.jwt.JWT;
import cn.hutool.jwt.JWTUtil;
import com.lsec.springbootinit.constant.CacheConstants;
import com.lsec.springbootinit.constant.Constants;
import com.lsec.springbootinit.interceptor.GlobalExceptionHandler;
import com.lsec.springbootinit.model.common.CommonResult;
import com.lsec.springbootinit.model.common.LoginUser;
import com.lsec.springbootinit.model.common.ServiceException;
import com.lsec.springbootinit.security.config.SecurityProperties;
import com.lsec.springbootinit.security.utils.SecurityFrameworkUtils;
import com.lsec.springbootinit.service.TokenService;
import com.lsec.springbootinit.utils.RedisCache;
import com.lsec.springbootinit.utils.ServletUtils;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import java.io.IOException;
import java.util.Objects;

/**
 * jwt身份验证令牌过滤器
 *
 * @author Cherry
 * @date 2023/06/26
 */
@Component
@RequiredArgsConstructor
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    private final RedisCache redisCache;
    private final SecurityProperties securityProperties;
    private final TokenService tokenService;
    private final GlobalExceptionHandler globalExceptionHandler;


    @Override
    @SuppressWarnings("NullableProblems")
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {

        //获取token
        String token = SecurityFrameworkUtils.obtainAuthorization(request, securityProperties.getTokenHeader());
        if (StringUtils.hasText(token)) {
            //解析token
            try {
                LoginUser loginUser = buildLoginUserByToken(token);
                if (!Objects.isNull(loginUser)) {

                    // 检测令牌是否自动续期
                    tokenService.verifyToken(loginUser);
                    // 存入SecurityContextHolder
                    SecurityFrameworkUtils.setLoginUser(loginUser, request);
                    //TODO  从redis中获取权限信息封装到Authentication中


                }

            } catch (Exception e) {
                // 统一错误类型
                CommonResult<?> result = globalExceptionHandler.allExceptionHandler(request, e);
                ServletUtils.writeJson(response, result);
                return;
            }

        }
        //放行
        filterChain.doFilter(request, response);
    }


    /**
     * 构建登录用户令牌
     *
     * @param token 令牌
     * @return {@link LoginUser}
     */
    private LoginUser buildLoginUserByToken(String token) {
        try {
            JWT jwt = JWTUtil.parseToken(token);
            NumberWithFormat userIdNum = (NumberWithFormat) jwt.getPayload(Constants.USER_ID);
            int userId = userIdNum.intValue();
            //从redis中获取用户信息
            return redisCache.getCacheObject(CacheConstants.AUTH_USER_KEY + userId);
        } catch (ServiceException serviceException) {
            // 校验 Token 不通过时，考虑到一些接口是无需登录的，所以直接返回 null 即可
            return null;
        }
    }


}